--[[getmetatable(_G).__newindex = function (table, key, val)
    error('Attempt to write to undeclared variable "' .. key .. '": '
          .. debug.traceback())
          end]]

local function do_abtest()

    local sid = ngx.var["gray_sid"]
    local engine_conf = require(string.format("gray_%s.engine_conf", sid))

    local function do_nick(nick)
        local function exists_in_tair(key)
            local etair = require("ngx.tair")
            local group = engine_conf.tair_group
            local tair, msg = etair:init(group)

            if not tair then
                ngx.log(ngx.ERR, msg)
                return false
            end

            local request_table = {data = { {key = key} } }
            local respdata, code, msg = tair:mget(engine_conf.tair_area, request_table)
            if code == 0 then
                if respdata.data[1].code == 0 and respdata.data[1].value then
                    return tonumber(string.match(respdata.data[1].value, "^B(%d)$"))
                else 
                    return false
                end
            else
                return false
            end
        end
        local key = ngx.var[nick.name] 
        if not key then return false, true end
        return exists_in_tair(string.format("%s%s", tostring(engine_conf.sid),
                                            key))
    end


    local function do_ip(ip)
        -- default ret is false
        if not ip.name then return false end
        if ngx.var[ip.name] == ip.value then
            return true
        else
            return false
        end
    end


    local function do_mod(mod)
        local function hash(str)
            local i = 1
            local key = tonumber(str)
            if key then return key end
            return ngx.crc32_short(str)
        end
        local key = ngx.var[mod.name]
        if not key then return false, true end
        local v = hash(key) % mod.max
        if v == mod.value and mod.cond == "==" then return true end
        if v >= mod.value and mod.cond == ">=" then return true end
        if v <= mod.value and mod.cond == "<=" then return true end
        if v > mod.value and mod.cond == ">" then return true end
        if v < mod.value and mod.cond == "<" then return true end
        return false
    end


    local function do_abck(abck, bs)
        if not abck then return false end
        local abck_value = ngx.var["cookie_"..abck.name] or ""
        if not abck_value or #abck_value < abck.index + 2 then return false end
        local index_value = string.sub(abck_value, abck.index + 1, abck.index + 1) 
        local index_num = tonumber(string.sub(abck_value, abck.index + 2, abck.index + 2))
        if not index_num then return false end
        if index_value == abck.a and index_num == 0 then
            return 0
        end
        if index_value == abck.b and (index_num >= 1 and index_num <= bs)then
            return index_num
        end
        return false
    end


    local function set_abck(abck, ab, seq)
        if not abck then return end
        local value
        if ab == "A" then value = abck.a .. seq else value = abck.b .. seq end
        local abck_value = ngx.var["cookie_"..abck.name] or ""
        if #abck_value >= abck.index + 2 then
            abck_value = string.sub(abck_value, 1, abck.index)..value..
                         string.sub(abck_value, abck.index + 3)
        else
            abck_value = string.sub(abck_value, 1, abck.index)..
                         string.rep(abck.o, abck.index - #abck_value)..value
        end
        abck_value = string.format("%s=%s; Path=%s", abck.name,
                                   abck_value, abck.path)
        if abck.domain and #abck.domain > 0 then
            abck_value = string.format("%s; Domain=%s", abck_value, abck.domain)
        end
        if abck.expire and abck.expire > 0 then
            abck_value = string.format("%s; expires=%s", abck_value,
                                       ngx.cookie_time(ngx.time() + abck.expire))
        end
        ngx.header["Set-Cookie"] = abck_value
    end


    local function set_server_name(sn, sid, seq)
        ngx.var[engine_conf.server_name_var] = sn
        local http_at_beta = "at_beta"
        if seq then
            ngx.header[http_at_beta] = sid .. "B" .. seq
        else
            ngx.header[http_at_beta] = sid .. "A"
        end
    end

    if ngx.var.arg_tb_eagleeyex_t == '1' or ngx.var.arg_tb_eagleeyex_t == '2' then
       ngx.header["at_beta"] = "gray disabled for yace (tb_eagleeyex_t)"
       return
    end

    if engine_conf.cell then
        local need_do_ab_ups = engine_conf.cell_local_ups
        if engine_conf.app_name then
            need_do_ab_ups = engine_conf.app_name .. "_" .. "local"
        end
        if ngx.var[engine_conf.server_name_var] ~= need_do_ab_ups then
            return
        end
    else
        ngx.var["gray_uri"] = ngx.raw_escape_uri(ngx.var.uri)
    end

    if type(engine_conf) ~= 'table' then return end
    --if not engine_conf.cell then
        set_server_name(engine_conf.asn, engine_conf.sid)
    --end
    -- TODO
    if not engine_conf.enable then return end
    -- test ab cookie
    local ret = do_abck(engine_conf.abck, #engine_conf.bs)
    if ret == 0 then
        return 
    end
    if ret ~= false then
        return set_server_name(engine_conf.bs[ret].sn, engine_conf.sid, ret)
    end
    ret = 0
    local nick = false
    local cookie_noexist
    for i, b in ipairs(engine_conf.bs) do
        local noexist
        if b.fcm.ip then
            ret = do_ip(b.fcm.ip)
            if ret then ret = i; break else ret = 0 end
        end
        if b.fcm.mod then
            ret, noexist = do_mod(b.fcm.mod)
            if noexist then cookie_noexist = true end
            if ret then ret = i; break else ret = 0 end
        end
        if b.fcm.nick then
            nick = b.fcm.nick
        end
    end
    if ret == 0 and nick then
        local noexist
        ret, noexist = do_nick(nick)
        if noexist then cookie_noexist = true end
        if not ret or (ret < 1 or ret > #engine_conf.bs) then
            ret = 0
        end
    end
    if ret == 0 then
        if not cookie_noexist then set_abck(engine_conf.abck, "A", ret) end
        return
    end
    set_abck(engine_conf.abck, "B", ret) 
    return set_server_name(engine_conf.bs[ret].sn, engine_conf.sid, ret)
end


local r, m = pcall(do_abtest)
if not r then
    ngx.log(ngx.ERR, m)
end
